﻿using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Windows.Foundation;
using Windows.UI.Xaml.Data;

namespace G.Data
{
    /// <summary>
    /// Utility class for implementing incremental loading.  Just subclass, and implement
    /// PullDataAsync().  Don't forget to use the async keyword!
    /// </summary>
    /// <typeparam name="T">Type of item in collection</typeparam>
    public abstract class IncrementalLoadingCollection<T>
        : ObservableCollection<T>, ISupportIncrementalLoading, INotifyPropertyChanged, IDisposable
    {
        public event PropertyChangedEventHandler PropertyChanged;
        public event EventHandler<IsLoadingDataChangedEventArgs> IsLoadingDataChanged;

        private uint _totalItemsCount = 0;
        private bool _hasMoreItems = true;
        private bool _isLoadingData = false;

        public IncrementalLoadingCollection()
        {
        }

        private uint _currentPageLowerBound = 0;

        public uint CurrentPageLowerBound
        {
            get
            {
                return _currentPageLowerBound;
            }
            set
            {
                _currentPageLowerBound = value;
            }
        }

        private uint _currentPageUpperBound = 0;

        public uint CurrentPageUpperBound
        {
            get
            {
                return _currentPageUpperBound;
            }
            set
            {
                _currentPageUpperBound = value;
            }
        }

        /// <summary>
        /// Total number of items of the collection; it's not the number of loaded items.
        /// </summary>
        public uint TotalItemsCount
        {
            get
            {
                return _totalItemsCount;
            }
            set
            {
                if (_totalItemsCount != value)
                {
                    _totalItemsCount = value;

                    if (PropertyChanged != null)
                    {
                        PropertyChanged(this, new PropertyChangedEventArgs("TotalItemsCount"));
                    }
                }
            }
        }

        /// <summary>
        /// Gets/sets whether the collection should continue loading data from source.        
        /// </summary>
        public bool HasMoreItems
        {
            get
            {
                return _hasMoreItems;
            }
            set
            {
                _hasMoreItems = value;
            }
        }

        /// <summary>
        /// IsLoadingData is true when data is actually being pulled (usually over a network).
        /// Useful with progress bars/rings.
        /// </summary>
        public bool IsLoadingData
        {
            get
            {
                return _isLoadingData;
            }
            set
            {
                if (_isLoadingData != value)
                {
                    bool oldValue = _isLoadingData;
                    bool newValue = value;
                    _isLoadingData = newValue;

                    if (PropertyChanged != null)
                    {
                        PropertyChanged(this, new PropertyChangedEventArgs("IsLoadingData"));
                    }

                    if (IsLoadingDataChanged != null)
                    {
                        IsLoadingDataChanged(this, new IsLoadingDataChangedEventArgs(oldValue, newValue));
                    }
                }
            }
        }
        public void Add(IEnumerable<T> items)
        {
            foreach (var item in items)
            {
                this.Add(item);
            }
        }
        public IAsyncOperation<LoadMoreItemsResult> LoadMoreItemsAsync(uint count)
        {
            return new IncrementalLoader<T>(this, count);
        }

        /// <summary>        
        /// PullDataAsync is where you actually get items from a data source (web service, database, XML, etc.)
        /// When implementing this method, make sure you make it async.
        /// </summary>
        /// <param name="currentPageLowerBound">Current page lower bound.</param>
        /// <param name="currentPageUpperBound">Current page upper bound.</param>
        /// <returns></returns>
        public abstract Task<IncrementalLoadingResult<T>> PullDataAsync(uint currentPageLowerBound, uint currentPageUpperBound);

        public abstract void Dispose();
    }
}
